/**
 * 
 */
package org.xlab.baseframework.dao.impl;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;
import java.util.Locale;

import org.apache.log4j.Logger;
import org.hibernate.EntityMode;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.MessageSource;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.xlab.baseframework.common.entity.PageinationEntity;
import org.xlab.baseframework.dao.api.IBaseDAO;
import org.xlab.baseframework.exception.ObjectNotFoundException;
import org.xlab.baseframework.exception.SystemRuntimeException;
import org.xlab.baseframework.exception.TooManyObjectsFoundException;
import org.xlab.baseframework.system.Declare;

/**
 * <p>
 * 范型 DAO实现类,系统中所有的DAO实现类应当继承自本类
 * </p>
 * 
 * @author MaKuBEX E-mail:makubexnet@gmail.com
 * @version 1.0.0 at Jul 3, 2009 5:10:10 PM
 * 
 */
@Repository
public abstract class BaseDAOImpl<T, PK extends java.io.Serializable> extends
		HibernateDaoSupport implements IBaseDAO<T, PK> {

	private static final Logger log = Logger.getLogger(BaseDAOImpl.class);

	protected Class<T> tClass;
	@Autowired(required = true)
	@Qualifier("bpf_daoMessageSource")
	protected MessageSource messageSource;

	/**
	 * 默认构造函数
	 */
	@SuppressWarnings("unchecked")
	public BaseDAOImpl() {
		this.tClass = (Class<T>) ((ParameterizedType) (this.getClass()
				.getGenericSuperclass())).getActualTypeArguments()[0];
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#findAll()
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAll() throws ObjectNotFoundException {
		try {
			String queryString = Declare.SQL_STATEMENT_FROM
					+ this.tClass.getName();
			List<T> result = super.getHibernateTemplate().find(queryString);
			if (result == null || result.size() == 0) {
				String message = this.messageSource.getMessage(
						"message.objexp.notfound",
						new Object[] { this.tClass.getName() },
						Locale.getDefault());
				log.debug(message);
				throw new ObjectNotFoundException(message);
			}
			return result;
		} catch (RuntimeException re) {
			String message = this.messageSource.getMessage(
					"message.objexp.failed", new Object[] { "Class" },
					Locale.getDefault());
			log.error(message, re);
			throw new SystemRuntimeException(message, re);
		}
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#findAll(org.xlab.baseframework.common.entity.PageinationEntity)
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAll(PageinationEntity page)
			throws ObjectNotFoundException {
		try {
			DetachedCriteria criteria = DetachedCriteria.forClass(this.tClass);
			List<T> result = super.getHibernateTemplate().findByCriteria(
					criteria, page.getStart(), page.getLimit());
			if (result == null || result.size() == 0) {
				String message = this.messageSource.getMessage(
						"message.objexp.notfound",
						new Object[] { this.tClass.getName() },
						Locale.getDefault());
				log.debug(message);
				throw new ObjectNotFoundException(message);
			}
			return result;
		} catch (RuntimeException re) {
			String message = this.messageSource.getMessage(
					"message.objexp.failed", new Object[] { "Class" },
					Locale.getDefault());
			log.error(message, re);
			throw new SystemRuntimeException(message, re);
		}
	}

	@SuppressWarnings("unchecked")
	protected List<T> findAllByCriteria(DetachedCriteria criteria) {
		log.debug("Executing HQL:-- " + criteria.toString() + " --");
		return super.getHibernateTemplate().findByCriteria(criteria);
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#findAllByExample(java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAllByExample(T exampleInstance)
			throws ObjectNotFoundException {
		try {
			List<T> result = super.getHibernateTemplate().findByExample(
					exampleInstance);
			if (result == null || result.size() == 0) {
				String message = this.messageSource.getMessage(
						"message.objexp.notfound.by.example",
						new Object[] { this.tClass.getName(),
								exampleInstance.toString() },
						Locale.getDefault());
				log.debug(message);
				throw new ObjectNotFoundException(message);
			}
			return result;
		} catch (RuntimeException re) {
			String message = this.messageSource.getMessage(
					"message.objexp.failed", new Object[] { "Example" },
					Locale.getDefault());
			log.error(message, re);
			throw new SystemRuntimeException(message, re);
		}

	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#findAllByExample(java.lang.Object,
	 *      org.xlab.baseframework.common.entity.PageinationEntity)
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAllByExample(T exampleInstance, PageinationEntity page)
			throws ObjectNotFoundException {
		try {
			List<T> result = null;
			if (page != null) {
				result = super.getHibernateTemplate().findByExample(
						exampleInstance, page.getStart(), page.getLimit());
				if (result == null || result.size() == 0) {
					String message = this.messageSource.getMessage(
							"message.objexp.notfound.by.example",
							new Object[] { this.tClass.getName(),
									exampleInstance.toString() },
							Locale.getDefault());
					log.debug(message);
					throw new ObjectNotFoundException(message);
				}
			} else {
				result = this.findAllByExample(exampleInstance);
			}
			return result;
		} catch (RuntimeException re) {
			String message = this.messageSource.getMessage(
					"message.objexp.failed", new Object[] { "Example" },
					Locale.getDefault());
			log.error(message, re);
			throw new SystemRuntimeException(message, re);
		}
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#findByProperty(java.lang.String,
	 *      java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAllByProperty(String propertyName, Object value)
			throws ObjectNotFoundException {
		if (propertyName == null || value == null)
			throw new NullPointerException();
		try {
			String queryString = Declare.SQL_STATEMENT_FROM
					+ this.tClass.getName() + Declare.SQL_STATEMENT_AS
					+ "model" + Declare.SQL_STATEMENT_WHERE + "model"
					+ Declare.DOT + propertyName + Declare.EQUAL + " ?";
			List<T> result = super.getHibernateTemplate().find(queryString,
					value);
			if (result == null || result.size() == 0) {
				String message = this.messageSource.getMessage(
						"message.objexp.notfound.by.property",
						new Object[] { this.tClass.getName(), propertyName,
								value.toString() }, Locale.getDefault());
				log.debug(message);
				throw new ObjectNotFoundException(message);
			}
			return result;
		} catch (RuntimeException re) {
			String message = this.messageSource.getMessage(
					"message.objexp.failed", new Object[] { "Property:"
							+ propertyName }, Locale.getDefault());
			log.error(message, re);
			throw new SystemRuntimeException(message, re);
		}
	}

	/**
	 * <p>
	 * 根据HQL语句查询对应的记录
	 * </p>
	 * 
	 * @param hql
	 *            HQL语句
	 * @return 符合查询条件的集合
	 * @throws ObjectNotFoundException
	 *             如果没有找到任何记录
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findAllByHql(String hql) throws ObjectNotFoundException {
		if (hql == null)
			throw new NullPointerException();
		try {
			log.debug("Executing HQL: -- " + hql + " --");
			List<T> result = getHibernateTemplate().find(hql);
			if (result == null || result.size() == 0) {
				log.debug("There are no result found for class["
						+ this.tClass.getName() + "] whit HQL[" + hql + "].");
				throw new ObjectNotFoundException(
						"There are no result found for class["
								+ this.tClass.getName() + "] whit HQL[" + hql
								+ "].");
			}
			return result;
		} catch (RuntimeException re) {
			String message = this.messageSource.getMessage(
					"message.objexp.failed", new Object[] { "Hql" },
					Locale.getDefault());
			log.error(message, re);
			throw new SystemRuntimeException(re);
		}
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#findById(java.io.Serializable)
	 */
	public T findById(PK id) throws ObjectNotFoundException {
		if (id == null)
			throw new NullPointerException();
		try {
			T result = this.getHibernateTemplate().get(this.tClass, id);
			if (result == null) {
				String message = messageSource.getMessage(
						"message.objexp.notfound.by.id", new Object[] {
								this.tClass.getName(), id },
						Locale.getDefault());
				log.debug(message);
				throw new ObjectNotFoundException(message);
			}
			return result;
		} catch (RuntimeException re) {
			String message = this.messageSource.getMessage(
					"message.objexp.failed", new Object[] { "Id" },
					Locale.getDefault());
			log.error(message, re);
			throw new SystemRuntimeException(message, re);
		}
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#findByUniqueProperty(java.lang.String,
	 *      java.lang.Object)
	 */
	public T findByUniqueProperty(String uniqueProperty, Object value)
			throws ObjectNotFoundException, TooManyObjectsFoundException {
		String message = null;
		if (uniqueProperty == null)
			throw new NullPointerException();
		try {
			T exampleInstance = tClass.newInstance();
			super.getSessionFactory()
					.getClassMetadata(this.tClass)
					.setPropertyValue(exampleInstance, uniqueProperty, value,
							EntityMode.POJO);
			List<T> results = this.findAllByExample(exampleInstance);
			if (results.size() > 1) {
				message = "There are " + results.size() + " results be found.";
				throw new TooManyObjectsFoundException(message);
			} else {
				return results.get(0);
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
			throw new SystemRuntimeException(e);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			throw new SystemRuntimeException(e);
		}
	}

	/**
	 * 构建DetachedCriteria实例
	 * 
	 * @return DetachedCriteria实例
	 */
	protected DetachedCriteria generateCriteria() {
		return DetachedCriteria.forClass(this.tClass);
	}

	/**
	 * @return the messageSource
	 */
	public MessageSource getMessageSource() {
		return messageSource;
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#merge(java.lang.Object)
	 */
	public T merge(T detachedInstance) {
		log.debug("merging " + detachedInstance.getClass() + " instance");
		try {
			T result = getHibernateTemplate().merge(detachedInstance);
			log.debug("merge successful");
			return result;
		} catch (RuntimeException re) {
			log.error("merge failed", re);
			throw new SystemRuntimeException(re);
		}
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#remove(java.lang.Object)
	 */
	public void remove(T persistentInstance) {
		log.debug("removing " + persistentInstance.getClass() + " instance");
		try {
			getHibernateTemplate().delete(persistentInstance);
			log.debug("remove successful");
		} catch (RuntimeException re) {
			log.error("remove failed", re);
			throw new SystemRuntimeException(re);
		}
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#removeAll(java.util.Collection)
	 */
	public void removeAll(Collection<T> persistentInstances) {
		log.debug("removing all " + persistentInstances.getClass()
				+ " instance");
		try {
			getHibernateTemplate().deleteAll(persistentInstances);
			log.debug("remove successful");
		} catch (RuntimeException re) {
			log.error("remove failed", re);
			throw new SystemRuntimeException(re);
		}
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#save(java.lang.Object)
	 */
	public void save(T transientInstance) {
		log.debug("saving " + transientInstance.getClass() + " instance");
		try {
			getHibernateTemplate().save(transientInstance);
			log.debug("save successful");
		} catch (RuntimeException re) {
			log.error("save failed", re);
			throw new SystemRuntimeException(re);
		}
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#saveOrUpdate(java.lang.Object)
	 */
	public void saveOrUpdate(T instance) {
		log.debug("saving or updating instance");
		try {
			getHibernateTemplate().saveOrUpdate(instance);
			log.debug("saving or updating successful");
		} catch (RuntimeException re) {
			log.error("saving or updating failed", re);
			throw new SystemRuntimeException(re);
		}
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#saveOrUpdateAll(java.util.Collection)
	 */
	public void saveOrUpdateAll(Collection<T> instanceList) {
		log.debug("saving or updating all instances");
		try {
			getHibernateTemplate().saveOrUpdateAll(instanceList);
			log.debug("saving or updating all successful");
		} catch (RuntimeException re) {
			log.error("saving or updating all failed", re);
			throw new SystemRuntimeException(re);
		}
	}

	/**
	 * @param messageSource
	 *            the messageSource to set
	 */
	public void setMessageSource(MessageSource messageSource) {
		this.messageSource = messageSource;
	}

	/**
	 * @see org.xlab.baseframework.dao.api.IBaseDAO#update(java.lang.Object)
	 */
	public void update(T persistentInstance) {
		log.debug("updating " + persistentInstance.getClass() + " instance");
		try {
			getHibernateTemplate().update(persistentInstance);
			log.debug("update successful");
		} catch (RuntimeException re) {
			log.error("update failed", re);
			throw new SystemRuntimeException(re);
		}
	}
}